#define  _CRT_SECURE_NO_WARNINGS 1
//#include"head.h"

/**
* 结构体声明、自引用、重命名、内存对齐
* 
*/


//struct tag
//{
//	member_list;
//}variable_list;


//struct Stu
//{
//	char name[20];
//	int age;
//	float score;
//}s3, s4;//全局变量
//
//struct Stu s2;//全局变量
//
//int main()
//{
//	struct Stu s1 = {"zhangsan", 18, 100.0f};//局部变量
//
//	return 0;
//}


//特殊声明
//struct //匿名结构体类型，不能在其他地方创建变量
//{
//	char name[20];
//	int age;
//	float score;
//}s1, s2;//只能这样创建变量
//
//struct
//{
//	char name[20];
//	int age;
//	float score;
//}*ps;
//
//int main()
//{
//	//ps = &s1;//error
//	//编译器认为左右两边是不相等的，类型不兼容
//
//	return 0;
//}


//结构体的自引用
//struct Node//（链表）
//{
//	int data;//数据域
//    struct Node* next;//指针域
//};
//
//int main()
//{
//	printf("%d\n", sizeof(struct Node));//16
//	/*
//	C 语言中，结构体的总大小并非简单的成员大小之和，而是要满足 
//	内存对齐规则：每个成员的起始地址必须是其自身大小的整数倍（目的是提升 CPU 访问效率）。 --> 空间换取时间
//
//	第一个成员 data（int，4 字节）：
//		从地址 0 开始存放（0 是 4 的整数倍，满足对齐），占用地址 0~3（4 字节）。
//	第二个成员 next（指针，8 字节）：
//		按规则，其起始地址必须是 8 的整数倍。但第一个成员结束于地址 3，下一个可用地址是 4，4 不是 8 的整数倍，
//		因此需要填充 4 字节的空白（Padding），将起始地址 “凑” 到 8（8 是 8 的整数倍）。
//		因此，next 从地址 8 开始存放，占用地址 8~15（8 字节）。
//	
//	成员实际占用（4 + 8）= 12 字节 + 填充的 4 字节 = 16 字节，且 16 是结构体最大成员大小（8 字节）的整数倍，满足整体对齐要求。
//	
//	内存对齐是编译器的自动行为，目的是让 CPU 更高效地访问内存（CPU 读取数据时通常按 “对齐的块” 读取，未对齐的地址可能需要多次访问）。
//	*/
//
//	return 0;
//}


////重命名：
//typedef struct Mode
//{
//	int data;
//	struct Mode* next;
//}Mode;
//
//错误格式：
//typedef struct Mode
//{
//	int data;
//	Mode* next;//不能未重命名时引用
//}Mode;
//
//typedef struct //更不能给匿名结构体这样重命名
//{
//	int data;
//	Mode* next;//不能未重命名时引用
//}Mode;


////结构体内存对齐：
//typedef struct S1
//{
//    char a;//1 --> 对齐数
//    char b;//1
//    int c;//4  --> 对齐数
//}S1;
//
//typedef struct S2
//{
//    char a;//1 --> 4
//    int c;//4
//    char b;//1 --> 4
//}S2;
//
//typedef struct S3
//{
//    double d; // --> 8
//    char c;  // -->1 -->4
//    int i;  // -->4
//}S3;
//
//typedef struct S4
//{
//    char c;  // --> 1 --> 8 (S3中最大成员对齐数)
//    struct S3 s3; // --> 16
//    double d;  // --> 8 
//}S4;
//
//int main()
//{
//    //printf("%d\n", sizeof(struct S1));//8
//    //printf("%d\n", sizeof(struct S2));//12
//
//    //结构体成员存储时，会按照内存对齐规则进行存储
//    //offsetof宏：获取结构体成员相对于结构体起始位置的偏移量
//    //offsetof(type(类型), member(成员名))
//
//    //printf("%d\n", offsetof(struct S1, a));//0
//    //printf("%d\n", offsetof(struct S1, b));//1
//    //printf("%d\n", offsetof(struct S1, c));//4
//    //printf("\n");
//    //printf("%d\n", offsetof(struct S2, a));//0
//    //printf("%d\n", offsetof(struct S2, c));//4
//    //printf("%d\n", offsetof(struct S2, b));//8
//
//    //========================================================================================================================
//
//    S1 st1;
//    S2 st2;
//    S3 st3;
//    S4 st4;
//
//    //内存对齐规则：
//    // 1. 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
//    // 2. 其他成员变量要对齐到某个数字（对齐数）的整数倍的地址处。（偏移量）
//    // 对齐数 = 编译器默认的一-个对齐数 与 该成员变量大小的较小值。
//    //  vs中 默认值为8
//    //  Linux中 gcc没有默认对齐数，对齐数就是成员自身大小
//    // 3. 结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数，所有对齐数中最大的)的整数倍。
//    // 4. 如果嵌套了结构体的情况，嵌套的结构体（里面的结构体）成员对齐到自己的成员中最大对齐数的整数倍处，
//    //    结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
//    // 
//    //
//
//    printf("%zd\n", sizeof(st1)); //8
//    printf("%zd\n", sizeof(st2)); //12  --> 让占用空间小的成员尽量集中在一起
//    printf("%zd\n", sizeof(st3)); //16
//    printf("%zd\n", sizeof(st4)); //32
//
//
//    return 0;
//} 



////修改默认对齐数
//// #pragma 这个预处理指令，可以改变编译器的默认对齐数
//#pragma pack(1) //设置默认对齐数为1字节
//struct S
//{
//    char a;
//    int c;
//    char b;
//    
//};
//#pragma pack() //恢复默认对齐数，还原为默认
//
//#pragma pack(2) //设置默认对齐数为2字节
//struct SS
//{
//    char a;
//    int c;
//    char b;
//
//};
//#pragma pack() //恢复默认对齐数，还原为默认
//
//int main()
//{
//    printf("%zd\n", sizeof(struct S)); //6
//    printf("%zd\n", sizeof(struct SS)); //8
//    return 0;
//}
////在结构体默认对齐数不合适时，可以通过#pragma pack(n)来设置对齐数 --> 一般都是2的次方数

